/* ***************************************************************************
 * This file is part of the NashCoding tutorial on SharpNEAT 2.
 * 
 * Copyright 2010, Wesley Tansey (wes@nashcoding.com)
 * 
 * Some code in this file may have been copied directly from SharpNEAT,
 * for learning purposes only. Any code copied from SharpNEAT 2 is 
 * copyright of Colin Green (sharpneat@gmail.com).
 *
 * Both SharpNEAT and this tutorial are free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 *
 * SharpNEAT is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SharpNEAT.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpNeat.EvolutionAlgorithms;
using SharpNeat.Genomes.Neat;
using SharpNeat.Decoders;
using SharpNeat.Core;
using SharpNeat.EvolutionAlgorithms.ComplexityRegulation;
using SharpNeat.Decoders.Neat;
using SharpNeat.Phenomes;
using SharpNeat.DistanceMetrics;
using SharpNeat.SpeciationStrategies;
using System.Xml;
using System.Runtime.InteropServices;


/// <summary>
/// Helper class that hides most of the details of setting up an experiment.
/// If you're just doing a simple console-based experiment, this is probably
/// what you want to inherit from. However, if you need more flexibility
/// (e.g., custom genome/phenome creation or performing complex population
/// evaluations) then you probably want to implement your own INeatExperiment
/// class.
/// </summary>
public abstract class SimpleNeatExperiment /*: INeatExperiment*/
{
	NeatEvolutionAlgorithmParameters _eaParams;
	NeatGenomeParameters _neatGenomeParams;
	string _name;
	int _populationSize;
	int _specieCount;
	NetworkActivationScheme _activationScheme;
	string _complexityRegulationStr;
	int? _complexityThreshold;
	string _description;



	#region Abstract properties that subclasses must implement
	public abstract IPhenomeEvaluator<IBlackBox> PhenomeEvaluator { get; }
	public abstract int InputCount { get; }
	public abstract int OutputCount { get; }
	public abstract bool EvaluateParents { get; }
	#endregion



	#region INeatExperiment Members
	public string Description
	{
	    get { return _description; }
	}

	public string Name
	{
	    get { return _name; }
	}

	/// <summary>
	/// Gets the default population size to use for the experiment.
	/// </summary>
	public int DefaultPopulationSize
	{
	    get { return _populationSize; }
	}

	/// <summary>
	/// Gets the NeatEvolutionAlgorithmParameters to be used for the experiment. Parameters on this object can be 
	/// modified. Calls to CreateEvolutionAlgorithm() make a copy of and use this object in whatever state it is in 
	/// at the time of the call.
	/// </summary>
	public NeatEvolutionAlgorithmParameters NeatEvolutionAlgorithmParameters
	{
	    get { return _eaParams; }
	}

	/// <summary>
	/// Gets the NeatGenomeParameters to be used for the experiment. Parameters on this object can be modified. Calls
	/// to CreateEvolutionAlgorithm() make a copy of and use this object in whatever state it is in at the time of the call.
	/// </summary>
	public NeatGenomeParameters NeatGenomeParameters
	{
	    get { return _neatGenomeParams; }
	}

	/// <summary>
	/// Initialize the experiment with some optional XML configutation data.
	/// </summary>
	public void Initialize(string name, XmlElement xmlConfig)
	{
	    _name = name;
	    _populationSize = EvolutionSettings.instance.PopulationSize;
		_specieCount = EvolutionSettings.instance.SpecieCount;
	    _activationScheme = ShittyFunctions.CreateActivationScheme(EvolutionSettings.instance.activationScheme);
		_complexityRegulationStr = EvolutionSettings.instance.ComplexityRegulationStrategy;
		_complexityThreshold = EvolutionSettings.instance.ComplexityThreshold;
		_description = EvolutionSettings.instance.description;

	    _eaParams = new NeatEvolutionAlgorithmParameters();
	    _eaParams.SpecieCount = _specieCount;
	    _neatGenomeParams = new NeatGenomeParameters();
	}

	[DllImport ("SharpNeatDomains")]
	public static extern List<NeatGenome> LoadPopulation(XmlReader xr, bool x, int inCnt, int outCnt);

	/// <summary>
	/// Load a population of genomes from an XmlReader and returns the genomes in a new list.
	/// The genome2 factory for the genomes can be obtained from any one of the genomes.
	/// </summary>
	public List<NeatGenome> LoadPopulation(XmlReader xr)
	{

			return LoadPopulation(xr, false, this.InputCount, this.OutputCount);
	}

	/// <summary>
	/// Save a population of genomes to an XmlWriter.
	/// </summary>
	public void SavePopulation(XmlWriter xw, IList<NeatGenome> genomeList)
	{
	    // Writing node IDs is not necessary for NEAT.
	    NeatGenomeXmlIO.WriteComplete(xw, genomeList, false);
	}

	/// <summary>
	/// Create a genome2 factory for the experiment.
	/// Create a genome2 factory with our neat genome2 parameters object and the appropriate number of input and output neuron genes.
	/// </summary>
	public IGenomeFactory<NeatGenome> CreateGenomeFactory()
	{
	    return new NeatGenomeFactory(InputCount, OutputCount, _neatGenomeParams);
	}

	/// <summary>
	/// Create and return a NeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. Various sub-parts
	/// of the algorithm are also constructed and connected up.
	/// This overload requires no parameters and uses the default population size.
	/// </summary>
	public NeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm()
	{
	    return CreateEvolutionAlgorithm(DefaultPopulationSize);
	}

	/// <summary>
	/// Create and return a NeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. Various sub-parts
	/// of the algorithm are also constructed and connected up.
	/// This overload accepts a population size parameter that specifies how many genomes to create in an initial randomly
	/// generated population.
	/// </summary>
	public NeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm(int populationSize)
	{
	    // Create a genome2 factory with our neat genome2 parameters object and the appropriate number of input and output neuron genes.
	    IGenomeFactory<NeatGenome> genomeFactory = CreateGenomeFactory();

	    // Create an initial population of randomly generated genomes.
	    List<NeatGenome> genomeList = genomeFactory.CreateGenomeList(populationSize, 0);

	    // Create evolution algorithm.
	    return CreateEvolutionAlgorithm(genomeFactory, genomeList);
	}

	/// <summary>
	/// Create and return a NeatEvolutionAlgorithm object ready for running the NEAT algorithm/search. Various sub-parts
	/// of the algorithm are also constructed and connected up.
	/// This overload accepts a pre-built genome2 population and their associated/parent genome2 factory.
	/// </summary>
	public NeatEvolutionAlgorithm<NeatGenome> CreateEvolutionAlgorithm(IGenomeFactory<NeatGenome> genomeFactory, List<NeatGenome> genomeList)
	{
	    // Create distance metric. Mismatched genes have a fixed distance of 10; for matched genes the distance is their weigth difference.
	    IDistanceMetric distanceMetric = new ManhattanDistanceMetric(1.0, 0.0, 10.0);
	    ISpeciationStrategy<NeatGenome> speciationStrategy = new KMeansClusteringStrategy<NeatGenome>(distanceMetric);

	    // Create complexity regulation strategy.
		IComplexityRegulationStrategy complexityRegulationStrategy = new DefaultComplexityRegulationStrategy(/*_complexityRegulationStr*/ComplexityCeilingType.Relative, System.Convert.ToDouble(_complexityThreshold));//TODO: Relative: verify if not broken
		// ExperimentUtils.CreateComplexityRegulationStrategy(_complexityRegulationStr, _complexityThreshold);

	    // Create the evolution algorithm.
	    NeatEvolutionAlgorithm<NeatGenome> ea = new HaxorsEvolutionAlgorithm<NeatGenome>(_eaParams, speciationStrategy, complexityRegulationStrategy);
		//TODO: make sure I haven't broke everything
	    // Create genome2 decoder.
	    IGenomeDecoder<NeatGenome, IBlackBox> genomeDecoder = new NeatGenomeDecoder(_activationScheme);
		//Vince says: Genome decoder for the game controller
		gameController.instance.genomeDecoder = new NeatGenomeDecoder(_activationScheme);

	    // Create a genome2 list evaluator. This packages up the genome2 decoder with the genome2 evaluator.
	    IGenomeListEvaluator<NeatGenome> genomeListEvaluator = new SerialGenomeListEvaluator<NeatGenome, IBlackBox>(genomeDecoder, PhenomeEvaluator);

	    // Wrap the list evaluator in a 'selective' evaulator that will only evaluate new genomes. That is, we skip re-evaluating any genomes
	    // that were in the population in previous generations (elite genomes). This is determiend by examining each genome2's evaluation info object.
	    if(!EvaluateParents)
	        genomeListEvaluator = new SelectiveGenomeListEvaluator<NeatGenome>(genomeListEvaluator,
	                                 SelectiveGenomeListEvaluator<NeatGenome>.CreatePredicate_OnceOnly());

	    // Initialize the evolution algorithm.
	    ea.Initialize(genomeListEvaluator, genomeFactory, genomeList);

	    // Finished. Return the evolution algorithm
	    return ea;
	}

	/// <summary>
	/// Creates a new genome decoder that can be used to convert a genome into a phenome.
	/// </summary>
	public IGenomeDecoder<NeatGenome, IBlackBox> CreateGenomeDecoder()
	{
	    return new NeatGenomeDecoder(_activationScheme);
	}

	#endregion
}
